package is.progetto1.aritmeticExpression;

import is.progetto1.exception.IllegalContextException;

public abstract class Expression implements Cloneable {

	public static final int INFIX_MODE = 0;
	public static final int POSTFIX_MODE = 1;
	public static final int ADD_MODE = 2;
	public static final int SUB_MODE = 3;
	public static final int MUL_MODE = 4;
	public static final int DIV_MODE = 5;

	protected Expression parent = null;

	public abstract int toAssess(Context c) throws IllegalContextException;

	public final Expression toReplace(Variable v, Expression e) {
		Expression res = this.toClone();
		this.toReplace(res, v, e);
		return res;
	}

	private final void toReplace(Expression index, Variable v, Expression e) {
		if (index instanceof Operator) {
			Operator op = (Operator) index;
			if (op.left != null && op.left.equals(v)) {
				op.setLeft(e);
				op.getLeft().parent = op;
			}
			if (op.right != null && op.right.equals(v)) {
				op.setRight(e);
				op.getRight().parent = op;
			}
			toReplace(op.left, v, e);
			toReplace(op.right, v, e);
		} else {
			return;
		}
	}

	public Expression toClone() {
		Expression res = null;
		try {
			res = (Expression) super.clone();
		} catch (CloneNotSupportedException e) {
			return null;
		}
		return res;
	}

	public final void accept(Visitor v, int mode) {
		switch (mode) {
		case INFIX_MODE:
			v.visitInfix(this);
			break;
		case POSTFIX_MODE:
			v.visitPostfix(this);
		default:
			throw new IllegalArgumentException();
		}
	}

	public Expression getLeft() {
		throw new UnsupportedOperationException();
	}

	public Expression getRight() {
		throw new UnsupportedOperationException();
	}

	public void setLeft(Expression e) {
		throw new UnsupportedOperationException();
	}

	public void setRight(Expression e) {
		throw new UnsupportedOperationException();
	}

	public final void setParent(Expression e) {
		this.parent = e;
	}

	public final Expression getParent() {
		return parent;
	}

	public abstract String toString();

	public abstract boolean equals(Object o);
}
